/* Proiect SPG Generator de particule
 * Ene Adriana
 * Tiriplica Diana
 * Grupa 342C4
 */
#include <stdlib.h>
#include <glut.h>
#include <stdio.h>
#include <stdarg.h>
#include <string.h>

#include "glaux.h"

#pragma comment( lib, "glaux.lib" ) 
// tasta escape
#define ESC	27

static int mainWindow;

// texturi particula
GLuint texture[10];

// numar de particule initiale
const int nr_particule = 1000;

// structura cu informatiile unei particule
typedef struct
{
	// pozitie particula
	double X;
	double Y;
	double Z;

	// translatie particula
	double miscareX;
	double miscareY;
	double miscareZ;

	//culoare particula
	double Red;
	double Green;
	double Blue;

	// rotatie particula
	double rotatieX;
	double rotatieY;
	double rotatieZ;

	// acceleratie si incetinire
	double speed_up;
	double speed_down;

	// scalare
	double Scalez;

	// vizibilitate
	bool Visible;
}Particula;

// un sistem de particule
Particula Sistem[nr_particule];


// functii ce incarca o textura
AUX_RGBImageRec *LoadBMP(char *Filename)				
{
	FILE *File=NULL;
	if (!Filename)										
	{
		return NULL;									
	}
	File=fopen(Filename,"r");							
	if (File)											
	{
		fclose(File);									
		return auxDIBImageLoad(Filename);				
	}
	return NULL;										
}

int LoadGLTextures(char *name)									
{
	int Status=FALSE;									
	AUX_RGBImageRec *TextureImage[1];					
	memset(TextureImage,0,sizeof(void *)*1);           	
	if (TextureImage[0]=LoadBMP(name))
	{
		Status=TRUE;									
		glGenTextures(1, &texture[0]);					
		//Textura nefiltrata
		glBindTexture(GL_TEXTURE_2D, texture[0]);
		glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
		glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
		glTexImage2D(GL_TEXTURE_2D, 0, 3, TextureImage[0]->sizeX, TextureImage[0]->sizeY, 0, GL_RGB, GL_UNSIGNED_BYTE, TextureImage[0]->data);
	}
	if (TextureImage[0])									
	{
		if (TextureImage[0]->data)							
		{
			free(TextureImage[0]->data);					
		}
		free(TextureImage[0]);								
	}
	return Status;										
}

// functie de initializare sistem de particule
void CreeazaParticule (void) {
	int i;
	for (i = 1; i < nr_particule; i++)
	{
		Sistem[i].X = 0;
		Sistem[i].Y = -5;
		Sistem[i].Z = -5;
		Sistem[i].miscareX = (((((((2 - 1 + 1) * rand()%11) + 1) - 1 + 1) *rand()%11) + 1) * 0.005) - (((((((2 - 1 + 1) * rand()%11) + 1) - 1 + 1
		) * rand()%11) + 1) * 0.005);
		Sistem[i].miscareY = (((((((2 - 1 + 1) * rand()%11) + 1) - 1 + 1) * rand()%11) + 1) * 0.005) - (((((((2 - 1 + 1) * rand()%11) + 1) - 1 + 1
		) * rand()%11) + 1) * 0.005);
		Sistem[i].miscareZ = (((((((2 - 1 + 1) * rand()%11) + 1) - 1 + 1) * rand()%11) + 1) * 0.005) - (((((((2 - 1 + 1) * rand()%11) + 1) - 1 + 1
		) * rand()%11) + 1) * 0.005);
		Sistem[i].Red = 0.5;
		Sistem[i].Green = 0.5;
		Sistem[i].Blue = 0.5;
		Sistem[i].Scalez = 0.25;
		Sistem[i].rotatieX = 0;
		Sistem[i].rotatieY = 0;
		Sistem[i].rotatieZ = 0;
		Sistem[i].speed_up = ((((((8 - 5 + 2) * rand()%11) + 5) - 1 + 1) * rand()%11) + 1) * 0.02;
		Sistem[i].speed_down = 0.0025;
	}
}

// functie ce updateaza informatiile particulelei
void Update (void) {
	int i;
	for (i = 1; i < nr_particule; i++)
	{

		glColor3f (Sistem[i].Red, Sistem[i].Green,Sistem[i].Blue);

		Sistem[i].Y = Sistem[i].Y + Sistem[i].speed_up - Sistem[i].speed_down;
		Sistem[i].speed_down = Sistem[i].speed_down +0.0025;

		Sistem[i].X = Sistem[i].X + Sistem[i].miscareX;
		Sistem[i].Y = Sistem[i].Y + Sistem[i].miscareY;
		Sistem[i].Z = Sistem[i].Z + Sistem[i].miscareZ;

		Sistem[i].rotatieX = Sistem[i].rotatieX + ((((((int)(0.5 - 0.1 + 0.1) * rand()%11) + 1) - 1 + 1) * rand()%11) + 1);

		if (Sistem[i].Y < -5)
		{
			Sistem[i].X = 0;
			Sistem[i].Y = -5;
			Sistem[i].Z = -5;
			Sistem[i].Red = 0.5;
			Sistem[i].Green = 0.5;
			Sistem[i].Blue = 0.5;
			Sistem[i].rotatieX = 0;
			Sistem[i].rotatieY = 0;
			Sistem[i].rotatieZ = 0;
			Sistem[i].speed_up = ((((((8 - 5 + 2) * rand()%11) + 5) - 1 + 1) * rand()%11) + 1) * 0.02;
			Sistem[i].speed_down = 0.0025;
		}

	}
}

// functie de desenare particule
void Draw (void) {
	int i;
	for (i = 1; i < nr_particule; i++)
	{
	glPushMatrix();
    glTranslatef (Sistem[i].X, Sistem[i].Y, Sistem[i].Z);
	glRotatef (Sistem[i].rotatieX - 90, Sistem[i].rotatieY, Sistem[i].rotatieZ, 1);   
    glScalef (Sistem[i].Scalez, Sistem[i].Scalez, Sistem[i].Scalez);
   
    glDisable (GL_DEPTH_TEST);
    glEnable (GL_BLEND);
        
    glBlendFunc (GL_ONE, GL_ONE);
    glBindTexture (GL_TEXTURE_2D, texture[0]);

    glBegin (GL_QUADS);
    glTexCoord2d (0, 0);
    glVertex3f (-1, -1, 0);
    glTexCoord2d (1, 0);
    glVertex3f (1, -1, 0);
    glTexCoord2d (1, 1);
    glVertex3f (1, 1, 0);
    glTexCoord2d (0, 1);
    glVertex3f (-1, 1, 0);
    glEnd();
    
    glBlendFunc (GL_ONE, GL_ONE);
    glBindTexture (GL_TEXTURE_2D, texture[1]);
    
    glBegin (GL_QUADS);
    glTexCoord2d (0, 0);
    glVertex3f (-1, -1, 0);
    glTexCoord2d (1, 0);
    glVertex3f (1, -1, 0);
    glTexCoord2d (1, 1);
    glVertex3f (1, 1, 0);
    glTexCoord2d (0, 1);
    glVertex3f (-1, 1, 0);
    glEnd();
        
    glEnable(GL_DEPTH_TEST);

	glPopMatrix();

	}
}

// functie in care se updadeaza si se deseneaza particulele
void display (void) {
    glClearDepth (1);
    glClearColor (0.0,0.0,0.0,1.0);
    glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glLoadIdentity();  
    glTranslatef (0,0,-10);
    Update();
    Draw();
    glutSwapBuffers();
}

// functie de initializare particule si texturi
void init (void) {
    glEnable( GL_TEXTURE_2D );
    glEnable(GL_DEPTH_TEST);
    CreeazaParticule();
	texture[0] = LoadGLTextures("particle.bmp");
	texture[1] = texture[0]; 
}

void reshape (int w, int h) {
    glViewport (0, 0, (GLsizei)w, (GLsizei)h);
    glMatrixMode (GL_PROJECTION);
    glLoadIdentity ();
    gluPerspective (60, (GLfloat)w / (GLfloat)h, 1.0, 100.0);
    glMatrixMode (GL_MODELVIEW);
}

int main (int argc, char **argv) {
    glutInit (&argc, argv);
    glutInitDisplayMode (GLUT_DOUBLE | GLUT_RGBA | GLUT_DEPTH);
    glutInitWindowSize (800, 600);
    glutInitWindowPosition (100, 100);
    glutCreateWindow ("Generator de particule");
    init();
    glutDisplayFunc (display);
    glutIdleFunc (display);
    glutReshapeFunc (reshape);
    glutMainLoop ();
    return 0;
}